package cn.kgc.springbootdemo.controller;

import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Validator;
import cn.hutool.core.util.StrUtil;
import cn.kgc.springbootdemo.constant.CommonConstants;
import cn.kgc.springbootdemo.enums.ResultEnum;
import cn.kgc.springbootdemo.exception.HandleException;
import cn.kgc.springbootdemo.service.MinioTemplate;
import cn.kgc.springbootdemo.util.*;
import com.google.common.collect.Maps;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.InputStream;
import java.util.Map;

/**
 * <h3>springbootdemo</h3>
 * <p>文件前端控制器</p>
 *
 * @author : DongZeJian
 * @date : 2020-12-22 17:41
 **/
@Api(value = "FileController",tags = "文件前端控制器")
@RestController
@RequestMapping("/file")
@RequiredArgsConstructor
@Validated
@Slf4j
public class FileController {

    @Autowired
    private  MinioTemplate minioTemplate;

    @Autowired
    private MinioURLUtils minioURLUtils;



    /**
     * 单文件上传
     *
     * @param file       文件名称
     * @param uploadType 系统模块 1:demo
     * @return
     */
    @ApiOperation(value = "上传单文件到资源中心", notes = "上传单文件到资源中心", produces = "application/json")
    @PostMapping(value = "/uploadFileToCenter")
    public R uploadFileToCenter(@ApiParam(value = "文件名称", required = true) @RequestParam("file") MultipartFile file,
                                @ApiParam(value = "系统模块 1:demo", required = true) @RequestParam("uploadType")@NotNull(message = "位置不能为空") Integer uploadType) {


        //获取bucketName 判断bucketName是否为后台指定
        String bucketName = Validator.validateNotEmpty(BucketUtils.getValue(uploadType), ResultEnum.PARAM_IS_ERROR.getMessage());

        //检查文件大小是否在指定范围内
        checkFileSize(bucketName, file);

        //更改文件名称
        String fileName = GetTime.getGuid() + StrUtil.DOT + FileUtil.extName(file.getOriginalFilename());

        //文件上传
        try {
            minioTemplate.putObject(bucketName,fileName,file.getInputStream());
        } catch (Exception e) {
            log.error("上传文件到Minio服务器异常 : {}", ExceptionUtil.stacktraceToString(e));
            throw new HandleException(ResultEnum.ERROR_CODE.getCode(), "上传文件到Minio服务器异常");
        }

        //返回路径给前端
        Map<String, String> map = Maps.newHashMap();
        map.put("fileName", fileName);
        map.put("effectiveUrl", minioTemplate.getObjectURL(bucketName, fileName, 7 * 24 * 60 * 60));
        map.put("permanentUrl", minioURLUtils.getMinioURl(fileName));
        return R.ok(map);
    }

    /**
     * 获取文件的URL访问地址
     *
     * @param fileName   文件名
     * @param uploadType 系统模块 1:dzj
     * @return
     */
    @ApiOperation(value = "获取文件的URL访问地址", notes = "获取文件的URL访问地址", produces = "application/json")
    @PostMapping("/getObjectURL")
    public R<String> getObjectURL(@ApiParam(value = "访问文件名", required = true)@NotBlank(message = "文件名称不能为空") @RequestParam String fileName,
                                  @ApiParam(value = "1:dzj", required = true)@NotNull(message = "系统模块不能为空") @RequestParam Integer uploadType,
                                  @ApiParam(value = "是否永久链接", required = true)@NotNull(message = "请选择是否要永久链接") @RequestParam Boolean permanentLink) {

        //获取bucketName 判断bucketName是否为后台指定
        String bucketName = Validator.validateNotEmpty(BucketUtils.getValue(uploadType), ResultEnum.PARAM_IS_ERROR.getMessage());

        //判断是否要永久链接
        if (permanentLink){
            return R.ok(minioURLUtils.getMinioURl(fileName));
        }
        //返回为期7天的链接
        return R.ok(minioTemplate.getObjectURL(bucketName, fileName, 7 * 24 * 60 * 60));
    }

    /**
     * 获取文件
     *
     * @param fileName 文件空间/名称
     * @param response
     * @return
     */
    @SneakyThrows
    @ApiOperation(value = "获取文件", notes = "获取文件", produces = "application/json")
    @GetMapping("/{fileName}/{uploadType}")
    public void file(@ApiParam(value = "访问文件名", required = true) @PathVariable(value = "fileName") String fileName,
                     @ApiParam(value = "系统模块 1:dzj", required = true) @PathVariable(value = "uploadType") Integer uploadType, HttpServletResponse response) {
        //获取bucketName 判断bucketName是否为后台指定
        String bucketName = Validator.validateNotEmpty(BucketUtils.getValue(uploadType), ResultEnum.PARAM_IS_ERROR.getMessage());

        try (InputStream inputStream = minioTemplate.getObject(bucketName, fileName)) {
            response.setContentType("application/octet-stream; charset=UTF-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
            IoUtil.copy(inputStream, response.getOutputStream());
        } catch (Exception e) {
            log.error("文件读取异常: {}", ExceptionUtil.stacktraceToString(e));
            HttpUtils.writeError(response, "文件读取异常");
            return;
        }
    }

    /**
     * 删除资源中心文件
     *
     * @param fileName   文件名称
     * @param uploadType 系统模块 1:upms 2:微信公众号
     * @return
     */
    @ApiOperation(value = "文件删除")
    @GetMapping("/deletefile/{fileName}/{uploadType}")
    public R<Boolean> deletefile(@ApiParam(value = "文件名称", required = true) @PathVariable(value = "fileName") String fileName,
                                 @ApiParam(value = "系统模块 1:pet", required = true) @PathVariable(value = "uploadType") Integer uploadType) {

        //获取bucketName 判断bucketName是否为后台指定
        String bucketName = Validator.validateNotEmpty(BucketUtils.getValue(uploadType), ResultEnum.PARAM_IS_ERROR.getMessage());

        //删除文件
        try {
            minioTemplate.removeObject(bucketName, fileName);
        } catch (Exception e) {
            log.error("文件删除异常: {}", ExceptionUtil.stacktraceToString(e));
            throw new HandleException(ResultEnum.ERROR_CODE.getCode(), "文件删除异常");
        }

        return R.ok(true);
    }

    /**
     * 根据bucket空间判断上传文件大小限制
     *
     * @param bucketName : Minio中bucket存储空间
     * @param file       : file文件
     */
    private void checkFileSize(String bucketName, MultipartFile file) {

        switch (bucketName) {

            //upms文件大小控制在3M以内
            case CommonConstants.BUCKET_DZJ:
                Boolean checkUpmsFileSize = FileCheckUtils.checkFileSize(file.getSize(), 1, "G");
                if (!checkUpmsFileSize) {
                    throw new HandleException(ResultEnum.ERROR_CODE.getCode(), file.getOriginalFilename() + " : 文件过大 请上传3M以内的文件");
                }
                break;

            default:
                break;
        }
    }
}
